/*
  these class are simple wrapper of IDiaSymbol, 
  for IDiaSymbol have too many functions which are ambiguous.

  to deal with array and pointer, here do some special thing that IDiaSymbol not give
  
  more info refer to <<dia_summary.txt>>
 */

#ifndef CDIAType_h_h
#define CDIAType_h_h

#pragma once

#include <vector>
#include <utility>

#include "dia2.h"

//#import "msdia80.dll" //compile error for msdia80.tlh
#include <comip.h>	// _com_ptr_t


#include "miscutil.h"

//_COM_SMARTPTR_TYPEDEF(IDiaSymbol, __uuidof(IDiaSymbol));
//
// !!!_COM_SMARTPTR_TYPEDEF compile error!!!
// use follows!
typedef _com_ptr_t<_com_IIID<IDiaSymbol, &__uuidof(IDiaSymbol)> > IDiaSymbolPtr;
typedef _com_ptr_t<_com_IIID<IDiaEnumSymbols, &__uuidof(IDiaEnumSymbols)> > IDiaEnumSymbolsPtr;

using namespace std;

class IEnumMemberData;
class CDclFixEx;

class IDIAType
{
public:
	~IDIAType(){};
public:
	virtual VOID Clone(IDIAType** ppIDIAType)=0;

public:
	virtual BOOL EnumMemberData(IEnumMemberData** ppEnum, BOOL* pfPtr=NULL)=0;

// "type"'s name related
public:
	// get the "real" type name
	virtual VOID GetTypeName(_tstring* pcstrName)=0;

// "type"'s property
public:
	virtual DWORD GetLen()=0;
	virtual BOOL IsConst()=0;
	virtual BOOL IsPtr()=0;
	virtual BOOL IsArray()=0;
	virtual BOOL IsEnum()=0;
	virtual DWORD GetBaseType()=0;	// return enum BasicType 
	virtual DWORD GetUdtKind()=0;


// friend class protected functions
friend class CPtrEnumMemberData;
friend class CArrayEnumMemberData;
protected:
	// get the delare operator's string
	virtual CDclFixEx* GetDclFixEx()=0;
};

//
// a wrapper of IDiaSymbol to represent "data"
class IDIAData
{
public:
	~IDIAData(){};

public:
	virtual VOID Clone(IDIAData** ppIDIAData){*ppIDIAData=new IDIAData;};

//
public:
	virtual IDIAType* GetType(){return NULL;};

//
// "data"'s property
public:
	virtual BOOL GetOffset(long* plOffset){return FALSE;};
	virtual BOOL IsStatic(){return FALSE;}; 
	virtual BOOL IsBit(){return FALSE;};
	virtual BOOL GetBitInfo(DWORD* pdwPos, DWORD* pdwLen){return FALSE;};

// "data"'s name related
public:
	virtual VOID GetDataName(_tstring* pcstrName){pcstrName->clear();};
	virtual VOID GetDataDescriptor(_tstring* pcstrDes);

};


//
// interface to enum member data
class IEnumMemberData
{
public:
	virtual ~IEnumMemberData(){};

public:
	// user should delete the pointer returned!
	virtual BOOL Next(IDIAData** ppDiaData)=0;
	virtual VOID Reset()=0;
};



//
// *, const*, &, [], (*fn) are declare fixers, these fixers need recurse to find the "real" type
enum enDclFixTp {DCL_FIX_NULL=0, DCL_FIX_PTR, DCL_FIX_CNST_PTR, DCL_FIX_REF, DCL_FIX_ARRAY, DCL_FIX_FUN};
class CDclFix
{
public:
	CDclFix(enDclFixTp tp=DCL_FIX_NULL, DWORD dwDim=0)
			:enType(tp), dwArrayDim(dwDim)
	{};
public:
	enDclFixTp  enType;
	DWORD	dwArrayDim;			// for array only

public:
	void AppendDclFixStr(_tstring* pstr);
};

class CDclFixEx: public CDclFix
{
public:
	CDclFixEx(IDiaSymbol* pIDIAType=NULL, enDclFixTp tp=DCL_FIX_NULL, DWORD dwDim=0)
			: pSymb(pIDIAType)
			, CDclFix(tp, dwDim)
	{};
public:
	IDiaSymbolPtr  pSymb;
};

//
// a wrapper of IDiaSymbol to represent "type"
class CDIAType:public IDIAType
{
public:
	CDIAType(IDiaSymbol* psymb);
protected:
	CDIAType(CDIAType& type)
		:m_pSymbDirectType(type.m_pSymbDirectType)
		,m_pSymbTrueType(type.m_pSymbTrueType)
		,m_vDclFixExS(type.m_vDclFixExS)
		{};

public:
	VOID Clone(IDIAType** ppIDIAType){*ppIDIAType=new CDIAType(*this);};

public:
	BOOL EnumMemberData(IEnumMemberData** ppEnum, BOOL* pfPtr=NULL);

// "type"'s name related
public:
	// get the "real" type name
	VOID GetTypeName(_tstring* pstrName);

// "type"'s property
public:
	DWORD GetLen();
	BOOL IsConst();
	BOOL IsPtr();
	BOOL IsArray();
	BOOL IsEnum();
	DWORD GetBaseType();	// return enum BasicType 
	DWORD GetUdtKind();

//
public:
	BOOL PopFixEx();

// for debug
public:
	BOOL GetTypeId(DWORD* pdwId);

protected:
	CDclFixEx* GetDclFixEx();
	VOID GetDclFixStr(_tstring* pcstrName);

// types maybe redundance 
protected:
	IDiaSymbolPtr m_pSymbDirectType;	// store the direct type
	std::vector<CDclFixEx> m_vDclFixExS;	// used for declare operation info
	IDiaSymbolPtr m_pSymbTrueType;	// store the final type
};

//
// type composed by isymbol type and operators
class CDIAComposeType:public CDIAType
{
public:
	CDIAComposeType(IDiaSymbol* psymb)
		:CDIAType(psymb)
		,m_dwAddFixNumb(0)
		,m_fConst(FALSE)
	{};

private:
	CDIAComposeType(CDIAComposeType& type)
		:CDIAType(type)
		,m_dwAddFixNumb(type.m_dwAddFixNumb)
		,m_fConst(type.m_fConst)
	{};
	
public:
	VOID Clone(IDIAType** ppIDIAType)
	{*ppIDIAType=new CDIAComposeType(*this);};


// "type"'s property
public:
	DWORD GetLen();
	BOOL IsConst();
	BOOL IsPtr();
	BOOL IsArray();

//
// to modify type
public:
	BOOL PushPtr(BOOL fConst=FALSE);
	BOOL PushDim(DWORD dwDim);
	BOOL PushFix(CDclFix* pFix);
	void SetConst(){m_fConst=TRUE;};

//
public:
	BOOL PopFixEx();

private:
	DWORD m_dwAddFixNumb;
	BOOL m_fConst;
};

class CDIAData:public IDIAData
{
public:
	CDIAData(IDiaSymbol* psymbolData);

public:
	VOID Clone(IDIAData** ppIDIAData){ *ppIDIAData=new CDIAData(m_pSymbData);};

public:
	IDIAType* GetType(){return m_autopType.get();};

//
// "data"'s property
public:
	BOOL GetOffset(long* plOffset);
	BOOL IsStatic(); 
	BOOL IsBit(){return m_fIsBitField;};
	BOOL GetBitInfo(DWORD* pdwPos, DWORD* pdwLen);
//	DWORD GetAccessType();

// "data"'s name related
public:
	VOID GetDataName(_tstring* pcstrName);

public:
	VOID SetDataArrayId(DWORD dwArrayId)
	{m_dwArryId=dwArrayId;};

// for debug
public:
	BOOL GetDataId(DWORD* pdwId);
	BOOL GetTypeId(DWORD* pdwId){return m_autopType->GetTypeId(pdwId);};

private:
	IDiaSymbolPtr m_pSymbData;	
	auto_ptr<CDIAType> m_autopType;

	DWORD m_dwArryId;	// for array data there should have a index
 
// for the facility of bit field	
	BOOL m_fIsBitField;
	DWORD m_dwBitPos;
	DWORD m_dwBitLen;
};

class CPseudoDIAData:public IDIAData
{
public:
	CPseudoDIAData(IDIAType* pDiaType, TCHAR* pName=NULL, DWORD dwOffset=0, BOOL fStatic=FALSE)
		:m_dwOffset(dwOffset)
		,m_fStatic(fStatic)
		,m_dwArryId(INVALID_DWORD)
	{
		if(pName)
		{
			m_strName=pName;
		}

		IDIAType* pIDIAType;
		pDiaType->Clone(&pIDIAType);
		m_autopDiaType.reset(pIDIAType);
	};

	CPseudoDIAData(IDiaSymbol* pSmbolType, TCHAR* pName=NULL, DWORD dwOffset=0, BOOL fStatic=FALSE)
		:m_dwOffset(dwOffset)
		,m_fStatic(fStatic)
		,m_dwArryId(INVALID_DWORD)
	{
		if(pName)
		{
			m_strName=pName;
		}

		m_autopDiaType.reset(new CDIAType(pSmbolType));
	};


	CPseudoDIAData(CPseudoDIAData& data)
		:m_strName(data.m_strName)
		,m_dwOffset(data.m_dwOffset)
		,m_fStatic(data.m_fStatic)
		,m_dwArryId(data.m_dwArryId)
	{
		IDIAType* pIDIAType;
		data.GetType()->Clone(&pIDIAType);
		m_autopDiaType.reset(pIDIAType);
	};
		
public:
	VOID Clone(IDIAData** ppIDIAData)
	{ *ppIDIAData=new CPseudoDIAData(*this);};

public:
	IDIAType* GetType(){return m_autopDiaType.get();};
//
// "data"'s property
public:
	BOOL GetOffset(long* plOffset);
	BOOL IsStatic(){return m_fStatic;}; 
	BOOL IsBit(){return false;};
	BOOL GetBitInfo(DWORD* pdwPos, DWORD* pdwLen){return false;};

// "data"'s name related
public:
	VOID GetDataName(_tstring* pstrName);

public:
	VOID SetDataArrayId(DWORD dwArrayId)
	{m_dwArryId=dwArrayId;};

private:
	auto_ptr<IDIAType> m_autopDiaType;

	_tstring m_strName;
	DWORD m_dwOffset;
	BOOL m_fStatic;
	
	DWORD m_dwArryId;	// for array data there should have a index
};

//
// enum udt data member
class CUDTEnumMemberData: public IEnumMemberData
{
public:
	CUDTEnumMemberData(IDiaSymbol* psymType)
	{
		VERIFY(SUCCEEDED(psymType->findChildren(SymTagBaseClass, NULL, nsNone, &m_pEnumBaseClass)));
		VERIFY(SUCCEEDED(psymType->findChildren(SymTagData, NULL, nsNone, &m_pEnumData)));
	};
	~CUDTEnumMemberData(){};
private:
	CUDTEnumMemberData();

public:
	virtual BOOL Next(IDIAData** ppDiaData);
	virtual VOID Reset(){m_pEnumBaseClass->Reset();m_pEnumData->Reset();};

private:
	IDiaEnumSymbolsPtr m_pEnumBaseClass;
	IDiaEnumSymbolsPtr m_pEnumData;
	
};

//
// enum a pointer's type
class CPtrEnumMemberData: public IEnumMemberData
{
public:
	CPtrEnumMemberData(IDIAType* pDiaType)
		: m_pDiaType(pDiaType), m_dwChldCnt(0)
	{ASSERT( (DCL_FIX_PTR==pDiaType->GetDclFixEx()->enType) 
			|| (DCL_FIX_CNST_PTR==pDiaType->GetDclFixEx()->enType) );};
private:
	CPtrEnumMemberData();

public:
	BOOL Next(IDIAData** ppDiaData);
	VOID Reset(){m_dwChldCnt=0;};

private:
	DWORD m_dwChldCnt;
	IDIAType* m_pDiaType;
};

//
// enum array stuff
class CArrayEnumMemberData: public IEnumMemberData
{
public:
	CArrayEnumMemberData(IDIAType* pDiaType)
		: m_pDiaType(pDiaType), m_dwCurId(0)
	{
		ASSERT(DCL_FIX_ARRAY==pDiaType->GetDclFixEx()->enType);
	};
	~CArrayEnumMemberData(){};
private:
	CArrayEnumMemberData(){};

public:
	BOOL Next(IDIAData** ppDiaData);
	VOID Reset(){m_dwCurId=0;};

private:
	IDIAType* m_pDiaType;
	DWORD m_dwCurId;
};

class CDIADataType
{
public:
	CDIADataType(IDiaSymbol* pSymbData, IDiaSymbol* pSymbType);
	CDIADataType(IDIAData* pIDiaData);
	CDIADataType(IDIAType* pIDiaType);

	~CDIADataType(){ if(m_pEnum){delete m_pEnum;} }
	
// "type"'s proterty
public:
	VOID GetTypeName(_tstring* pcstrName){m_pType->GetTypeName(pcstrName);};

	DWORD GetLen(){ return m_pType->GetLen();};
	BOOL IsConst(){ return m_pType->IsConst();};
	BOOL IsPtr(){ return m_pType->IsPtr();};
	BOOL IsArray(){ return m_pType->IsArray();};
	BOOL IsEnum(){ return m_pType->IsEnum();};
	DWORD GetBaseType(){ return m_pType->GetBaseType();};	// return enum BasicType 
	DWORD GetUdtKind(){ return m_pType->GetUdtKind();};

//
// "data"'s property
public:
	BOOL GetOffset(long* plOffset){ return m_pData->GetOffset(plOffset);};
	BOOL IsStatic(){ return m_pData->IsStatic();}; 
	BOOL IsBit(){ return m_pData->IsBit();};
	BOOL GetBitInfo(DWORD* pdwPos, DWORD* pdwLen){ return m_pData->GetBitInfo(pdwPos, pdwLen);};
	VOID GetDataName(_tstring* pcstrName){ m_pData->GetDataName(pcstrName);};
	VOID GetDataDescriptor(_tstring* pcstrDes){ m_pData->GetDataDescriptor(pcstrDes);};


public:
	// user should delete the pointer returned!
	BOOL NextChild(CDIADataType** ppDiaDataType, BOOL* pfPtr=NULL);
	VOID Reset();

private:
	auto_ptr<IDIAData>	 m_pData;
	auto_ptr<IDIAType>	 m_pType;

	IEnumMemberData* m_pEnum;
};


#endif
